﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Runtime.Serialization;
using System.Net;
using System.IO;
using System.Xml;
using System.Web.Script.Serialization;
using System.Globalization;

namespace Geocoda
{

    public class GeocodaClient
    {
        string Apikey;
        public GeocodaClient(string apikey)
        {
            if (String.IsNullOrEmpty(apikey))
                throw new ArgumentException("API key cannot be empty or null");

            Apikey = apikey;
        }
        JavaScriptSerializer js = new JavaScriptSerializer();
        //*************** by giving adress as parameter it returns latitude and longitude*************
        public Geocodainfo LookupCoordinates(string address)
        {
            if (String.IsNullOrEmpty(address))
                throw new ArgumentException("address cannot be empty or null");

            string result = null;
            string url = "https://api.geocoda.com/v1/loc/geocode/" + address;
            HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
            request.Accept = "*/*";
            request.Method = "GET";
            request.Credentials = new NetworkCredential(Apikey, "x");
            using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
            {
                if (response != null)
                {
                    Stream receiveStream = response.GetResponseStream();

                    using (StreamReader sr = new StreamReader(receiveStream))
                    {
                        result = sr.ReadToEnd();
                    }
                }
                response.Close();

            }
            Geocodainfo newresult = js.Deserialize<Geocodainfo>(result);
            return newresult;

        }
        //****************by giving latitude and longitude as parameters it returns address***********
        public Geocodainfo LookupAddress(double latitude, double longitude)
        {
            if (latitude == null)
                throw new ArgumentException("latitude cannot be empty or null");
            if (longitude == null)
                throw new ArgumentException("longitude cannot be empty or null");

            string result = null;
            string lat = latitude.ToString(CultureInfo.InvariantCulture);
            string lon = longitude.ToString(CultureInfo.InvariantCulture);
            string url = "https://api.geocoda.com/v1/loc/coord/" + lat + "," + lon;
            HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
            request.Accept = "*/*";
            request.Method = "GET";
            request.Credentials = new NetworkCredential(Apikey, "x");
            using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
            {
                if (response != null)
                {
                    Stream receiveStream = response.GetResponseStream();

                    using (StreamReader sr = new StreamReader(receiveStream))
                    {
                        result = sr.ReadToEnd();
                    }
                }
                response.Close();

            }
            Geocodainfo newresult = js.Deserialize<Geocodainfo>(result);
            return newresult;
        }
        //****************it returns all the layers***********************
        public Geocodainfo GetLayers()
        {

            string result = null;
            HttpWebRequest request = WebRequest.Create("https://api.geocoda.com/v1/storage/layers?limit=100&offset=0") as HttpWebRequest;
            request.Accept = "*/*";
            request.Method = "GET";
            request.Credentials = new NetworkCredential(Apikey, "x");
            using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
            {
                if (response != null)
                {
                    Stream receiveStream = response.GetResponseStream();
                    using (StreamReader sr = new StreamReader(receiveStream))
                    {
                        result = sr.ReadToEnd();
                    }
                }
                response.Close();
            }
            Geocodainfo newresult = js.Deserialize<Geocodainfo>(result);
            return newresult;
        }
        //****************it returns the layers by limit and offeset values********
        public Geocodainfo GetLayers(int limit, int offset)
        {
            if (limit == null)
                throw new ArgumentException("limit cannot be empty or Null");
            if (offset == null)
                throw new ArgumentException("offset cannot be empty or Null");
            string result = null;
            string lmt = limit.ToString(CultureInfo.InvariantCulture);
            string ofst = offset.ToString(CultureInfo.InvariantCulture);
            HttpWebRequest request = WebRequest.Create("https://api.geocoda.com/v1/storage/layers?limit=" + lmt + "&offset=" + ofst) as HttpWebRequest;
            request.Accept = "*/*";
            request.Method = "GET";
            request.Credentials = new NetworkCredential(Apikey, "x");
            using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
            {
                if (response != null)
                {
                    Stream receiveStream = response.GetResponseStream();
                    using (StreamReader sr = new StreamReader(receiveStream))
                    {
                        result = sr.ReadToEnd();
                    }
                }
                response.Close();
            }
            Geocodainfo newresult = js.Deserialize<Geocodainfo>(result);
            return newresult;
        }
        //****************it returns the specified layer*****************
        public Geocodainfo GetLayer(string layername)
        {
            if (String.IsNullOrEmpty(layername))
                throw new ArgumentException("layer name cannot be empty or null");
            string result = null;
            HttpWebRequest request = WebRequest.Create("https://api.geocoda.com/v1/storage/layers/" + layername) as HttpWebRequest;
            request.Accept = "*/*";
            request.Method = "GET";
            request.Credentials = new NetworkCredential(Apikey, "x");
            using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
            {
                if (response != null)
                {
                    Stream receiveStream = response.GetResponseStream();
                    using (StreamReader sr = new StreamReader(receiveStream))
                    {
                        result = sr.ReadToEnd();
                    }
                }
                response.Close();

            }
            Geocodainfo newresult = js.Deserialize<Geocodainfo>(result);
            return newresult;
        }
        //**************** it returns all the records present in the specified layer********
        public Geocodainfo GetRecords(string layername)
        {
            if (String.IsNullOrEmpty(layername))
                throw new ArgumentException("layer name cannot be empty or Null");
            string result = null;
            HttpWebRequest request = WebRequest.Create("https://api.geocoda.com/v1/storage/records/" + layername + "?limit=100&offset=0") as HttpWebRequest;
            request.Accept = "*/*";
            request.Method = "GET";
            request.Credentials = new NetworkCredential(Apikey, "x");
            using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
            {
                if (response != null)
                {
                    Stream receiveStream = response.GetResponseStream();
                    using (StreamReader sr = new StreamReader(receiveStream))
                    {
                        result = sr.ReadToEnd();
                    }
                }
                response.Close();
            }
            Geocodainfo newresult = js.Deserialize<Geocodainfo>(result);
            return newresult;
        }
        //****************it returns all the records by limit and offset values present in the specified layer********
        public Geocodainfo GetRecords(string layername, int limit, int offset)
        {
            if (limit == null)
                throw new ArgumentException("limit cannot be empty or Null");
            if (offset == null)
                throw new ArgumentException("offset cannot be empty or Null");
            if (String.IsNullOrEmpty(layername))
                throw new ArgumentException("layer name cannot be empty or Null");
            string result = null;
            string lmt = limit.ToString(CultureInfo.InvariantCulture);
            string ofst = offset.ToString(CultureInfo.InvariantCulture);
            HttpWebRequest request = WebRequest.Create("https://api.geocoda.com/v1/storage/records/" + layername + "?limit=" + lmt + "&offset=" + ofst) as HttpWebRequest;
            request.Accept = "*/*";
            request.Method = "GET";
            request.Credentials = new NetworkCredential(Apikey, "x");
            using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
            {
                if (response != null)
                {
                    Stream receiveStream = response.GetResponseStream();
                    using (StreamReader sr = new StreamReader(receiveStream))
                    {
                        result = sr.ReadToEnd();
                    }
                }
                response.Close();
            }
            Geocodainfo newresult = js.Deserialize<Geocodainfo>(result);
            return newresult;
        }
        //****************it returns the specified record in the specified layer*************
        public Geocodainfo GetRecord(string layername, string recordname)
        {
            if (String.IsNullOrEmpty(layername))
                throw new ArgumentException("layer name cannot be empty or null");
            if (String.IsNullOrEmpty(recordname))
                throw new ArgumentException("record name cannot be empty or null");
            string result = null;
            HttpWebRequest request = WebRequest.Create("https://api.geocoda.com/v1/storage/records/" + layername + "/" + recordname) as HttpWebRequest;
            request.Accept = "*/*";
            request.Method = "GET";
            request.Credentials = new NetworkCredential(Apikey, "x");
            using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
            {
                if (response != null)
                {
                    Stream receiveStream = response.GetResponseStream();
                    using (StreamReader sr = new StreamReader(receiveStream))
                    {
                        result = sr.ReadToEnd();
                    }
                }
                response.Close();

            }
            Geocodainfo newresult = js.Deserialize<Geocodainfo>(result);
            return newresult;

        }
        //****************it returns the specified record history from specified layer*****
        public Geocodainfo GetRecordHistory(string layername, string recordname)
        {
            if (String.IsNullOrEmpty(layername))
                throw new ArgumentException("layer name cannot be empty or null");
            if (String.IsNullOrEmpty(recordname))
                throw new ArgumentException("record name cannot be empty or null");
            string result = null;
            HttpWebRequest request = WebRequest.Create("https://api.geocoda.com/v1/storage/records/" + layername + "/" + recordname + "/history") as HttpWebRequest;
            request.Accept = "*/*";
            request.Method = "GET";
            request.Credentials = new NetworkCredential(Apikey, "x");
            using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
            {
                if (response != null)
                {
                    Stream receiveStream = response.GetResponseStream();
                    using (StreamReader sr = new StreamReader(receiveStream))
                    {
                        result = sr.ReadToEnd();
                    }
                }
                response.Close();

            }
            Geocodainfo newresult = js.Deserialize<Geocodainfo>(result);
            return newresult;
        }
        //****************it saves specified record in the specified layer**********************
        public Geocodainfo SavePoint(string layername, string recordname, double latitude, double longitude)
        {
            if (String.IsNullOrEmpty(layername))
                throw new ArgumentException("layer name cannot be empty or null");
            if (String.IsNullOrEmpty(recordname))
                throw new ArgumentException("record name cannot be empty or null");
            if (latitude == null)
                throw new ArgumentException("latitude cannot be empty or null");
            if (longitude == null)
                throw new ArgumentException("longitude cannot be empty or null");

            string strdata = null;
            string strresponse = null;
            string lat = latitude.ToString(CultureInfo.InvariantCulture);
            string lon = longitude.ToString(CultureInfo.InvariantCulture);
            strdata = "{\"type\":\"Feature\",\"geometry\": {\"type\": \"Point\",\"coordinates\": [lon,lat]}}";
            strdata = strdata.Replace("lat", lat);
            strdata = strdata.Replace("lon", lon);
            HttpWebRequest request = WebRequest.Create("https://api.geocoda.com/v1/storage/records/" + layername + "/" + recordname) as HttpWebRequest;
            request.Method = "POST";
            request.Accept = "*/*";
            request.ContentType = "application/x-www-form-urlencoded";
            byte[] byteArray = Encoding.ASCII.GetBytes(strdata);
            request.ContentLength = byteArray.Length;

            request.Credentials = new NetworkCredential(Apikey, "x");

            Stream dataStream = request.GetRequestStream();
            dataStream.Write(byteArray, 0, byteArray.Length);
            dataStream.Close();
            WebResponse response = request.GetResponse();
            dataStream = response.GetResponseStream();
            StreamReader reader = new StreamReader(dataStream);
            string responseFromServer = reader.ReadToEnd();
            strresponse = responseFromServer;
            reader.Close();
            dataStream.Close();
            response.Close();
            Geocodainfo newresult = js.Deserialize<Geocodainfo>(strresponse);
            return newresult;

        }
        //****************it update the layer if exists or create new one with description************************
        public Geocodainfo SaveLayer(string layername, string description)
        {
            if (String.IsNullOrEmpty(layername))
                throw new ArgumentException("layer name cannot be empty or null");
            if (String.IsNullOrEmpty(description))
                throw new ArgumentException("description cannot be empty or null");
            string strdata = null;
            string strresponse = null;
            strdata = "{\"description\":\"descriptionlayer\"}";
            strdata = strdata.Replace("descriptionlayer", description);
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://api.geocoda.com/v1/storage/layers/" + layername);
            request.Method = "POST";
            request.Accept = "*/*";
            request.ContentType = "application/x-www-form-urlencoded";
            request.Credentials = new NetworkCredential(Apikey, "x");
            byte[] byteArray = Encoding.ASCII.GetBytes(strdata);
            request.ContentLength = byteArray.Length;
            Stream dataStream = request.GetRequestStream();
            dataStream.Write(byteArray, 0, byteArray.Length);
            dataStream.Close();
            WebResponse response = request.GetResponse();
            dataStream = response.GetResponseStream();
            StreamReader reader = new StreamReader(dataStream);
            string responseFromServer = reader.ReadToEnd();
            strresponse = responseFromServer;
            reader.Close();
            dataStream.Close();
            response.Close();
            Geocodainfo newresult = js.Deserialize<Geocodainfo>(strresponse);
            return newresult;
        }
        //****************it update the record if exists or create new one with specified latitude and longitude**********
        public Geocodainfo SaveRecord(string layername, string recordname, double lat, double lon)
        {
            if (String.IsNullOrEmpty(layername))
                throw new ArgumentException("layer name cannot be empty or null");
            if (String.IsNullOrEmpty(recordname))
                throw new ArgumentException("record name cannot be empty or null");
            if (lat == null)
                throw new ArgumentException("latitude cannot be empty or null");
            if (lon == null)
                throw new ArgumentException("longitude cannot be empty or null");

            string strdata = null;
            string strresponse = null;
            string latitude = lat.ToString(CultureInfo.InvariantCulture);
            string longitude = lon.ToString(CultureInfo.InvariantCulture);
            strdata = "{\"type\":\"Feature\",\"geometry\": {\"type\": \"Point\",\"coordinates\": [lon,lat]}}";
            strdata = strdata.Replace("lat", latitude);
            strdata = strdata.Replace("lon", longitude);
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://api.geocoda.com/v1/storage/records/" + layername + "/" + recordname);
            request.Method = "POST";
            request.Accept = "*/*";
            request.ContentType = "application/x-www-form-urlencoded";
            request.Credentials = new NetworkCredential(Apikey, "x");
            byte[] byteArray = Encoding.ASCII.GetBytes(strdata);
            request.ContentLength = byteArray.Length;
            Stream dataStream = request.GetRequestStream();
            dataStream.Write(byteArray, 0, byteArray.Length);
            dataStream.Close();
            WebResponse response = request.GetResponse();
            dataStream = response.GetResponseStream();
            StreamReader reader = new StreamReader(dataStream);
            string responseFromServer = reader.ReadToEnd();
            strresponse = responseFromServer;
            reader.Close();
            dataStream.Close();
            response.Close();
            Geocodainfo newresult = js.Deserialize<Geocodainfo>(strresponse);
            return newresult;
        }
        //****************it deletes the specified layer********************
        public Geocodainfo DeleteLayer(string layername)
        {
            if (String.IsNullOrEmpty(layername))
                throw new ArgumentException("layer name cannot be empty or null");
            string result = null;
            HttpWebRequest request = WebRequest.Create("https://api.geocoda.com/v1/storage/layers/" + layername) as HttpWebRequest;
            request.Accept = "*/*";
            request.Method = "DELETE";
            request.Credentials = new NetworkCredential(Apikey, "x");
            using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
            {
                if (response != null)
                {
                    Stream receiveStream = response.GetResponseStream();

                    using (StreamReader sr = new StreamReader(receiveStream))
                    {
                        result = sr.ReadToEnd();
                    }
                }
                response.Close();

            }
            Geocodainfo newresult = js.Deserialize<Geocodainfo>(result);
            return newresult;

        }
        //****************it deletes the specified record in the specified layer****************
        public Geocodainfo DeleteRecord(string layername, string recordname)
        {
            if (String.IsNullOrEmpty(layername))
                throw new ArgumentException("layer name cannot be empty or null");
            if (String.IsNullOrEmpty(recordname))
                throw new ArgumentException("record name cannot be empty or null");
            string result = null;
            HttpWebRequest request = WebRequest.Create("https://api.geocoda.com/v1/storage/records/" + layername + "/" + recordname) as HttpWebRequest;
            request.Accept = "*/*";
            request.Method = "DELETE";
            request.Credentials = new NetworkCredential(Apikey, "x");
            using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
            {
                if (response != null)
                {
                    Stream receiveStream = response.GetResponseStream();

                    using (StreamReader sr = new StreamReader(receiveStream))
                    {
                        result = sr.ReadToEnd();
                    }
                }
                response.Close();

            }
            Geocodainfo newresult = js.Deserialize<Geocodainfo>(result);
            return newresult;

        }
        //****************it updates multiple records if exists or creates new records with specified record names and latitude and longitude********
        public string SaveRecords(string layername, List<Records> objRecords)
        {
            if (String.IsNullOrEmpty(layername))
                throw new ArgumentException("layer name cannot be empty or null");
            string strresponse = null;
            int intrecordscount = 0;
            string recordsupdate = "0 records updated.";
            foreach (Records objRecord in objRecords)
            {
                intrecordscount = intrecordscount + 1;

                string strdata = null;
                strdata = "{\"type\":\"Feature\",\"geometry\": {\"type\": \"Point\",\"coordinates\": [lon,lat]}}";
                strdata = strdata.Replace("lat", objRecord.Latitude.ToString(CultureInfo.InvariantCulture));
                strdata = strdata.Replace("lon", objRecord.Longitude.ToString(CultureInfo.InvariantCulture));
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://api.geocoda.com/v1/storage/records/" + layername + "/" + objRecord.RecordName);
                request.Method = "POST";
                request.Accept = "*/*";
                request.ContentType = "application/x-www-form-urlencoded";
                request.Credentials = new NetworkCredential(Apikey, "x");
                byte[] byteArray = Encoding.ASCII.GetBytes(strdata);
                request.ContentLength = byteArray.Length;
                Stream dataStream = request.GetRequestStream();
                dataStream.Write(byteArray, 0, byteArray.Length);
                dataStream.Close();
                WebResponse response = request.GetResponse();
                strresponse = strresponse + " " + (((HttpWebResponse)response).StatusDescription);
                dataStream = response.GetResponseStream();
                StreamReader reader = new StreamReader(dataStream);
                string responseFromServer = reader.ReadToEnd();
                strresponse += responseFromServer;
                reader.Close();
                dataStream.Close();
                response.Close();
            }
            return recordsupdate.Replace("0", intrecordscount.ToString(CultureInfo.InvariantCulture));
        }
        //****************it returns the nearby records by latitude and longitude from the specified layer*****
        public Geocodainfo GetNearbyRecords(string layername, double latitude, double longitude, int radius)
        {
            if (String.IsNullOrEmpty(layername))
                throw new ArgumentException("layer name cannot be empty or null");
            if (radius == null)
                throw new ArgumentException("radius cannot be empty or null");
            if (latitude == null)
                throw new ArgumentException("latitude cannot be empty or null");
            if (longitude == null)
                throw new ArgumentException("longitude cannot be empty or null");

            string lat = latitude.ToString(CultureInfo.InvariantCulture);
            string lon = longitude.ToString(CultureInfo.InvariantCulture);
            string rad = radius.ToString(CultureInfo.InvariantCulture);
            string url = "https://api.geocoda.com/v1/storage/records/{layer}/nearby/{lat},{lon}.json?radius=" + rad;
            url = url.Replace("{lat}", lat);
            url = url.Replace("{lon}", lon);
            url = url.Replace("{layer}", layername);

            string result = null;
            HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
            request.Accept = "*/*";
            request.Method = "GET";
            request.Credentials = new NetworkCredential(Apikey, "x");
            using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
            {
                if (response != null)
                {
                    Stream receiveStream = response.GetResponseStream();

                    using (StreamReader sr = new StreamReader(receiveStream))
                    {
                        result = sr.ReadToEnd();
                    }
                }
                response.Close();
            }
            Geocodainfo newresult = js.Deserialize<Geocodainfo>(result);
            return newresult;
        }
        //****************it returns the nearby records by latitude and longitude from the specified layer***********
        public Geocodainfo GetNearbyBoundingBox(string layername, double latitude, double longitude, double swlat, double swlon, double nelat, double nelon)
        {
            if (String.IsNullOrEmpty(layername))
                throw new ArgumentException("layer name cannot be empty or null");
            if (latitude == null)
                throw new ArgumentException("latitude cannot be empty or null");
            if (longitude == null)
                throw new ArgumentException("longitude cannot be empty or null");
            if (swlat == null)
                throw new ArgumentException("swlat cannot be empty or null");
            if (swlon == null)
                throw new ArgumentException("swlon cannot be empty or null");
            if (nelat == null)
                throw new ArgumentException("nelat cannot be empty or null");
            if (nelon == null)
                throw new ArgumentException("nelon cannot be empty or null");
            string lat = latitude.ToString(CultureInfo.InvariantCulture);
            string lon = longitude.ToString(CultureInfo.InvariantCulture);
            string swlatitde = swlat.ToString(CultureInfo.InvariantCulture);
            string swlongitude = swlon.ToString(CultureInfo.InvariantCulture);
            string nelatitude = nelat.ToString(CultureInfo.InvariantCulture);
            string nelongitude = nelon.ToString(CultureInfo.InvariantCulture);
            string url = "https://api.geocoda.com/v1/storage/records/{layer}/nearby/{lat},{lon}.json?bounding_box={swlat},{swlon},{nelat},{nelon}";
            url = url.Replace("{lat}", lat);
            url = url.Replace("{lon}", lon);
            url = url.Replace("{layer}", layername);
            url = url.Replace("{swlat}", swlatitde);
            url = url.Replace("{swlon}", swlongitude);
            url = url.Replace("{nelat}", nelatitude);
            url = url.Replace("{nelon}", nelongitude);
            string result = null;
            HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
            request.Accept = "*/*";
            request.Method = "GET";
            request.Credentials = new NetworkCredential(Apikey, "x");
            using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
            {
                if (response != null)
                {
                    Stream receiveStream = response.GetResponseStream();

                    using (StreamReader sr = new StreamReader(receiveStream))
                    {
                        result = sr.ReadToEnd();
                    }
                }
                response.Close();
            }
            Geocodainfo newresult = js.Deserialize<Geocodainfo>(result);
            return newresult;
        }

    }
    public class Geocodainfo
    {
        public double timestamp;
        public Query query = new Query();
        public Address address = new Address();
        public List<Feature> features = new List<Feature>();
        public List<Layer> layers = new List<Layer>();
        public string offset;
        public string name;
        public string description;
        public string created_at;
        public string updated_at;
        public List<Record> records = new List<Record>();
        public string update;
        public string type;
        public List<Geometries> geometries = new List<Geometries>();
        public string id;
        public Geometry geometry = new Geometry();
        public Property properties = new Property();
    }
    public class Record
    {
        public string id;
        public Geometry geometry = new Geometry();
        public Property properties = new Property();
    }
    public class Layer
    {
        public string name;
        public string description;
        public string created_at;
        public string updated_at;
    }
    public class Query
    {
        public string address;
        public double longitude;
        public double latitude;
        public double precision;
        public string confidence;
    }
    public class Address
    {
        public string type;
        public Property properties = new Property();
        public Geometry geometry = new Geometry();
    }
    public class Property
    {
        public string created_at;
        public string updated_at;
        public string layer;
        public string country;
        public string city;
        public string postcode;
        public string county;
        public string distance;
        public string province;
    }
    public class Geometry
    {
        public string type;
        public double[] coordinates;
        public Property properties = new Property();
    }
    public class Geometries
    {
        public string type;
        public double[] coordinates;
        public string created_at;
        public string updated_at;
    }
    public class Feature
    {
        public List<Classifier> classifiers = new List<Classifier>();
        public string abbr;
        public string name;
    }
    public class Classifier
    {
        public string type;
        public string category;
        public string subcategory;
    }

}
